package org.rayson.server.transport;

import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Properties;

import org.rayson.server.Container;
import org.rayson.server.Manager;
import org.rayson.share.transport.NioChannel;
import org.rayson.share.transport.NioSelector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServerSelector extends NioSelector implements Manager {
	private static Logger LOGGER = LoggerFactory
			.getLogger(ServerSelector.class);

	private boolean running;
	private final ServerSocketChannel socketChannel;
	private Thread thread;

	private final Container container;

	public ServerSelector(final Selector selector, final String name,
			final ServerSocketChannel socketChannel, final Container container) {
		super(selector, name);
		this.socketChannel = socketChannel;
		this.container = container;
	}

	@Override
	protected void doAccept(final SelectionKey key) {
		final ServerSocketChannel channel = (ServerSocketChannel) key.channel();
		// NioConnection connection = null;

		try {
			final SocketChannel clientChannel = channel.accept();
			clientChannel.configureBlocking(false);
			// register read events for the new connection.
			final SelectionKey clientKey = clientChannel.register(selector,
					SelectionKey.OP_READ);
			final NioChannel nioChannel = new NioChannel(clientKey,
					clientChannel);
			final ServerConnection connection = container
					.getConnectionManager().newConnection(nioChannel);
			clientKey.attach(connection);
			container.getConnectionManager().add(connection);
			LOGGER.info("new connection: " + connection + " accepted.");

		} catch (final IOException e) {
			LOGGER.error("Accept new connection error", e);
			return;
		}

	}

	@Override
	protected void doRead(final SelectionKey key) {

		final ServerConnection connection = (ServerConnection) key.attachment();
		try {
			connection.asyncRead();

		} catch (final IOException e) {
			try {
				connection.close();
			} catch (final IOException e1) {
				// Do nothing about this exception.
			} finally {
				container.getConnectionManager().recycle(connection);

			}
		}

	}

	@Override
	protected void doWrite(final SelectionKey key) {
		final ServerConnection connection = (ServerConnection) key.attachment();
		try {
			connection.asyncWrite();
		} catch (final IOException e) {
			try {
				connection.close();
			} catch (final IOException e1) {
				// Do nothing about this exception.
			} finally {
				container.getConnectionManager().recycle(connection);

			}
		}
	}

	@Override
	public Container getContainer() {
		return this.container;
	}

	@Override
	public void run() {
		SelectionKey sk = null;
		while (running) {

			try {

				pollSupplementReads();
				pollSupplementWrites();
				selector.select();

				for (final Iterator<SelectionKey> iter = selector
						.selectedKeys().iterator(); iter.hasNext();) {
					sk = iter.next();
					iter.remove();
					try {

						if (!sk.isValid())
							continue;
						if (sk.isAcceptable())
							doAccept(sk);
						else if (sk.isReadable())
							doRead(sk);
						else if (sk.isWritable())
							doWrite(sk);

					} catch (final Exception e) {
						e.printStackTrace();
						continue;
					}

				}

			} catch (final OutOfMemoryError e) {

			} catch (final Throwable e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}

	@Override
	public void start(final Properties properties)
			throws ClosedChannelException {
		this.running = true;
		this.socketChannel.register(selector, SelectionKey.OP_ACCEPT);
		this.thread = new Thread(this, this.getName());
		this.thread.setDaemon(false);
		this.thread.start();
	}

	@Override
	public void stop() {
		// TODO Auto-generated method stub

	}
}
